import {Vector2, Vector3, BufferGeometry, Object3D,InstancedMesh,Mesh,Matrix4} from "three";

export interface IModel extends Object3D{
	metadata: Object; // 用于存储模型的元数据
}

// 获取场景中所有的动画
export function getAnimations() {
	const animations: any = []; // 创建一个数组存储动画

	// 遍历场景中的所有对象
	window.editor.scene.traverse(function (object) {
		animations.push(...object.animations);
	});

	return animations;
}

// 获取材质的名称
export function getMaterialName(material) {
	// 如果是材质数组
	if (Array.isArray(material)) {
		const array:any = [];
		// 遍历材质数组
		for (let i = 0; i < material.length; i++) {
			array.push(material[i].name);
		}

		return array.join(',');
	}

	return material.name; // 如果是单个材质，直接返回其名称
}

// 获取对象的类型
export function getObjectType( object ) {
	if ( object.isScene ) return 'Scene'; // 场景对象
	if ( object.isCamera ) return 'Camera'; // 相机对象
	if ( object.isLight ) return 'Light'; // 光源对象
	if ( object.isMesh ) return 'Mesh'; // 网格对象
	if ( object.isLine ) return 'Line'; // 线对象
	if ( object.isPoints ) return 'Points'; // 点对象

	return 'Object3D'; // 默认返回基础3D对象类型
}

/**
 * 将InstancedMesh（实例化网格）转换为普通Mesh数组
 * @param instancedMesh 实例化网格
 * @returns Mesh[] 普通Mesh数组
 */
export function getMeshByInstancedMesh(instancedMesh:InstancedMesh){
	const meshes:Mesh[] = []; // 存储转换后的网格数组
	// if (instancedMesh.material === undefined) return meshes;

	const matrixWorld = instancedMesh.matrixWorld; // 获取实例化网格的世界矩阵
	const count = instancedMesh.count; // 获取实例数量

	// 遍历每个实例
	for (let instanceId = 0; instanceId < count; instanceId++) {
		const _mesh = new Mesh(); // 创建新的网格对象
		const _instanceLocalMatrix = new Matrix4(); // 创建局部变换矩阵
		const _instanceWorldMatrix = new Matrix4(); // 创建世界变换矩阵

		// 复制几何体和材质
		_mesh.geometry = instancedMesh.geometry;
		_mesh.material = instancedMesh.material;

		// 计算每个实例的世界矩阵
		instancedMesh.getMatrixAt(instanceId, _instanceLocalMatrix);
		// 计算世界矩阵
		_instanceWorldMatrix.multiplyMatrices(matrixWorld, _instanceLocalMatrix);

		// 网格表示这个单一实例 // 设置网格的世界矩阵
		_mesh.matrixWorld = _instanceWorldMatrix;

		meshes.push(_mesh); // 将转换后的网格添加到数组
	}

	return meshes; // 返回转换后的网格数组
}

/**
 * 获取当前选中模型 path
 */
export function getSelectedModelPath() {
	let pathArr:string[] = []; // 存储路径数组

	// 递归获取父对象的名称
	function getPath(obj) {
		if (obj.parent) {
			pathArr.unshift(obj.parent.name); // 将父对象名称添加到数组开头
			getPath(obj.parent); // 继续向上递归
		}
	}

	getPath(window.editor.selected); // 从选中对象开始获取路径

	return pathArr.join(' !! '); // 用 !! 连接路径返回
}

// 获取鼠标在DOM元素中的归一化坐标
export function getMousePosition(dom: HTMLElement, x: number, y: number) {
	const rect = dom.getBoundingClientRect(); // 获取元素的边界框
	return [(x - rect.left) / rect.width, (y - rect.top) / rect.height]; // 返回归一化的坐标 [0-1]范围
}

// 将屏幕坐标转换为世界坐标
export function screenToWorld(x:number, y:number) {
	const vector = new Vector3(); // 创建三维向量
	// 将屏幕坐标转换为标准化设备坐标（NDC）
	vector.set(
		(x / window.viewer.container.offsetWidth) * 2 - 1,
		-(y / window.viewer.container.offsetHeight) * 2 + 1,
		0.5
	);
	vector.unproject(window.editor.camera); // 将NDC坐标转换为世界坐标

	// 计算从相机到点的方向向量
	const dir = vector.sub(window.editor.camera.position).normalize();
	// 计算到z平面的距离
	const distance = -window.editor.camera.position.z / dir.z;
	// 返回世界坐标点
	return window.editor.camera.position.clone().add(dir.multiplyScalar(distance));
}

// 重新计算几何体的UV坐标
export function reBufferGeometryUv(geometry:BufferGeometry) {
	const uv = geometry.attributes.uv; // 获取UV属性
	if(!uv) return; // 如果没有UV属性则返回
 
	// 创建用于存储UV范围的对象
	// 获取u和v的范围
	const box = {
		min: new Vector2(Infinity, Infinity),
		max: new Vector2(-Infinity, -Infinity),
	};
	// 遍历所有UV坐标找出范围
	for (let i = 0; i < uv.count; i++) {
		const u = uv.getX(i);
		const v = uv.getY(i);

		// 更新最小最大值
		box.min.x = Math.min(box.min.x, u);
		box.min.y = Math.min(box.min.y, v);
		box.max.x = Math.max(box.max.x, u);
		box.max.y = Math.max(box.max.y, v);
	}

	// 计算偏移量和范围
	const offset = new Vector2(0 - box.min.x, 0 - box.min.y);
	const range = new Vector2(box.max.x - box.min.x, box.max.y - box.min.y);

	// 遍历顶点，修改uv  // 重新计算每个UV坐标，使其归一化到[0,1]范围
	for (let i = 0; i < uv.count; i++) {
		const u = uv.getX(i);
		const v = uv.getY(i);

		// 计算新的u和v
		const newU = (u + offset.x) / range.x;
		const newV = (v + offset.y) / range.y;

		// 写入新的uv
		uv.setXY(i, newU, newV);
	}

	// 通知three.js更新
	uv.needsUpdate = true; // 标记UV需要更新
}

/**
 * 判断是否是group,因为导入有可能存在被定义为Object3D类型的group
 * 判断对象是否是组
 */
export function isGroup(object3D){
	// 通过isGroup属性或是否有子对象来判断
	return (object3D.isGroup || object3D.children.length > 0)
}

// 设置对象的用户数据
export function setUserData(object:IModel, key:string, value:any) {
	// key按照.分割，设置到object的userData中
	const keys = key.split('.');
	let obj = object.userData; // 获取userData对象
	// 遍历创建嵌套对象
	for (let i = 0; i < keys.length - 1; i++) { 
		const k = keys[i];
		if (!obj[k]) {
			obj[k] = {}; // 如果不存在则创建新对象
		}
		obj = obj[k];
	}
	// 设置最终值
	obj[keys[keys.length - 1]] = value;
}

// 设置对象的元数据
export function setMetaData(object:IModel, key:string, value:any) {
	// key按照.分割，设置到object的metaData中
	const keys = key.split('.'); // 将键按点分割
	let metadata = object.metadata; // 获取metadata对象

	// 如果metadata不存在则创建
	if (!metadata) {
		metadata = {};
		object.metadata = metadata;
	}

	for (let i = 0; i < keys.length - 1; i++) {
		const k = keys[i];
		if (!metadata[k]) {
			metadata[k] = {}; // 如果不存在则创建新对象
		}
		metadata = metadata[k];
	}
	// 设置最终值
	metadata[keys[keys.length - 1]] = value;
}

/**
 * 获取画布的截屏图片
 * @param canvas 画布对象
 * @returns Promise<HTMLImageElement> 截屏的图片对象
 */
export function getViewportImage(canvas: HTMLCanvasElement) {
	/*
	return new Promise<HTMLImageElement>((resolve,rejcet) => {
		// 将画布转换为Blob对象
		canvas.toBlob((blob) => {
			if (blob === null) {
				rejcet('截屏失败');
				return;
			}
			// 创建新的图片对象
			const image = new Image();
			image.src = URL.createObjectURL(blob); // 设置图片源为Blob URL

			resolve(image); // 返回图片对象
		});
	});
	*/
	return new Promise<HTMLImageElement>((resolve, reject) => {
    const dataUrl = canvas.toDataURL(); // 将画布转为Base64编码
    const image = new Image();
    image.src = dataUrl; // 使用Base64编码的图片数据

    image.onload = () => {
      resolve(image); // 返回图片对象
    };

    image.onerror = (error) => {
      reject('截屏失败');
    };
  });
}